Skip to content

feat: add mcp:// discovery URI resolution (draft-serra-mcp-discovery-uri)#952

Closed
vincenzopalazzo wants to merge 1 commit into
modelcontextprotocol:mainfrom
vincenzopalazzo:feat/mcp-discovery-uri
Closed

feat: add mcp:// discovery URI resolution (draft-serra-mcp-discovery-uri)#952
vincenzopalazzo wants to merge 1 commit into
modelcontextprotocol:mainfrom
vincenzopalazzo:feat/mcp-discovery-uri

Conversation

@vincenzopalazzo

Copy link
Copy Markdown

Summary

Implements the client-side resolver for IETF draft-serra-mcp-discovery-uri: resolve an mcp://host[:port][/path] URI to a working MCP server endpoint.

This was originally submitted as a goose core PR (aaif-goose/goose#9660) but @DOsinga correctly identified that the resolver is generic plumbing with nothing goose-specific — it belongs here in rmcp.

Resolution chain:

  1. (optional fast-mode) DNS TXT _mcp.{host} for endpoint hint, _mcp-key.{host} for JWK public key used to verify manifest signatures
  2. (authoritative) GET https://{host}/.well-known/mcp-server manifest
  3. (fallback) direct handshake probe at https://{host}/mcp

Security posture: HTTPS-only; endpoint host must equal-or-be-subdomain of discovery host; detached JWS signature verified against DNS-published JWK; fail-closed when a manifest claims a signature we cannot verify.

Feature-gated behind discovery (DNS + HTTP resolver) and discovery-jws (adds JWS signature verification via jsonwebtoken).

cc @alexhancock

Review Notes

  • Production-safety review passed (2 rounds)
  • Code is fully unit-tested with mock DNS and HTTP backends (32 tests pass)
  • 3 E2E tests are #[ignore]d — they need real TLS infrastructure to test HTTPS resolution against a live server
  • Adapted to hickory-resolver 0.26 API (builder_tokio() requires system-config feature; txt_data is a field not a method)
  • All exported types are #[non_exhaustive] per rmcp's exhaustive_structs / exhaustive_enums clippy lints
  • cargo clippy -D warnings clean, cargo fmt applied

Decision Log

Hardest decision: Marking the 3 E2E tests (e2e_well_known_manifest_resolution, e2e_direct_fallback_resolution, e2e_no_server_found) as #[ignore] rather than trying to set up a self-signed TLS server in unit tests. The resolver always builds https:// URLs from mcp:// URIs (correct per spec), but spinning up a TLS-enabled axum server with self-signed certs in a unit test adds significant complexity for marginal coverage — the resolution logic is fully exercised by the 32 mock-based tests. The E2E tests are left in place for manual/integration testing.

Alternatives rejected:

  • HTTP fallback for localhost: Adding a special case to allow http:// for 127.0.0.1 / localhost would violate the spec's HTTPS-only requirement and create a security hole.
  • rustls danger_insecure_verify: Using reqwest's insecure TLS mode in tests would require a separate feature gate and is fragile.

Least confident about: The JWS canonicalization in jws.rs — it's a JCS-style approximation (recursive key sort + compact serialization) that covers typical manifests but does not implement full RFC 8785 number/unicode normalization. A mismatch fails closed (rejects a valid signature), never the reverse, but a reviewer with crypto expertise should confirm this is acceptable for the draft stage.

Test plan

  • CI passes
  • cargo test -p rmcp --features "discovery,discovery-jws" --lib discovery — 32 tests pass, 3 ignored
  • cargo clippy -p rmcp --features "discovery,discovery-jws" -- -D warnings — clean
  • cargo fmt --check — clean

…uri)

Implements the client-side resolver for IETF draft-serra-mcp-discovery-uri:
resolve an mcp://host[:port][/path] URI to a working MCP server endpoint.

Resolution chain:
1. (optional fast-mode) DNS TXT _mcp.{host} for endpoint hint,
   _mcp-key.{host} for JWK public key used to verify manifest signatures
2. (authoritative) GET https://{host}/.well-known/mcp-server manifest
3. (fallback) direct handshake probe at https://{host}/mcp

Security posture: HTTPS-only; endpoint host must equal-or-be-subdomain of
discovery host; detached JWS signature verified against DNS-published JWK;
fail-closed when a manifest claims a signature we cannot verify.

Feature-gated behind 'discovery' (DNS+HTTP resolver) and 'discovery-jws'
(adds JWS signature verification via jsonwebtoken).

Co-Authored-By: goose <goose@aaif.ai>
@vincenzopalazzo vincenzopalazzo requested a review from a team as a code owner July 2, 2026 18:47
@github-actions github-actions Bot added T-dependencies Dependencies related changes T-test Testing related changes T-config Configuration file changes T-core Core library changes labels Jul 2, 2026
@alexhancock

Copy link
Copy Markdown
Contributor

Hi @vincenzopalazzo thanks for the submission but given this is in I-D I think we'll hold off to see if it progresses. Sorry for the misleading note that this would be the natural home for it.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-config Configuration file changes T-core Core library changes T-dependencies Dependencies related changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants